package com.wootion.cims.exception;

import com.auth0.jwt.exceptions.TokenExpiredException;
import com.wootion.cims.response.ResponseResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ValidationException;
import java.io.IOException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.sql.SQLException;
import java.util.List;

/**
 * 全局异常处理器
 */
@Slf4j
@RestControllerAdvice()
@SuppressWarnings("all")
public class GlobalExceptionHandler {

    /**
     * 全局系统异常，未知异常统一处理
     *
     * @return
     */
    @ResponseStatus(HttpStatus.OK)
//    @ExceptionHandler(value = Exception.class)
    public ResponseResult errorHandler(Exception e, HttpServletRequest request) {
        if (e instanceof NoHandlerFoundException) {
            log.info("server error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return ResponseResult.builder().code((long) HttpServletResponse.SC_NOT_FOUND)
                    .message("找不到请求资源").build();
        } else if (e instanceof MissingServletRequestParameterException) {
            log.info("server error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return ResponseResult.builder().code((long) HttpServletResponse.SC_BAD_REQUEST)
                    .message("缺少参数").build();
        } else if (e instanceof MaxUploadSizeExceededException) {
            log.info("server error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return ResponseResult.builder().code((long) HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE)
                    .message("文件上传超出大小限制").build();
        } else if (e instanceof SQLException) {
            log.info("server error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return ResponseResult.builder().code((long) HttpServletResponse.SC_MULTIPLE_CHOICES)
                    .message("数据库操作失败").build();
        } else if (e instanceof SocketTimeoutException) {
            log.info("server error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return ResponseResult.builder().code((long) HttpServletResponse.SC_PARTIAL_CONTENT)
                    .message("服务连接超时").build();
        } else if (e instanceof SocketException) {
            log.info("server error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return ResponseResult.builder().code((long) HttpServletResponse.SC_NO_CONTENT)
                    .message("服务连接失败").build();
        } else if (e instanceof IOException) {
            log.info("server error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return ResponseResult.builder().code((long) HttpServletResponse.SC_BAD_REQUEST)
                    .message("系统错误").build();
        } else {
            log.info("server error start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), e.getClass(), e.getMessage());
            return ResponseResult.builder().code((long) HttpServletResponse.SC_INTERNAL_SERVER_ERROR)
                    .message("系统错误").build();
        }
    }

    /**
     * 处理自定义业务异常
     *
     * @param request
     * @param ex
     * @return
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(BusinessException.class)
    public ResponseResult handleBusinessException(HttpServletRequest request, BusinessException ex) {
        log.info("BusinessException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), ex.getClass(), ex.getMessage());
        return ResponseResult.builder().code((long) HttpServletResponse.SC_INTERNAL_SERVER_ERROR)
                .message(ex.getMessage()).build();
    }

    /**
     * 参数绑定异常
     *
     * @param request
     * @param ex
     * @return
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(BindException.class)
    public ResponseResult handleBindException(HttpServletRequest request, BindException ex) {
        log.info("BindException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), ex.getClass(), ex.getMessage());
        return ResponseResult.builder().code((long) HttpServletResponse.SC_NOT_ACCEPTABLE)
                .message("请求参数格式错误").build();
    }

    /**
     * 不支持的请求方式异常
     *
     * @param request
     * @param ex
     * @return
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseResult handleHttpRequestMethodNotSupportedException(HttpServletRequest request, HttpRequestMethodNotSupportedException ex) {
        log.info("HttpRequestMethodNotSupportedException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), ex.getClass(), ex.getMessage());
        return ResponseResult.builder().code((long) HttpServletResponse.SC_METHOD_NOT_ALLOWED)
                .message("请求方式不支持").build();
    }

    /**
     * 不支持的请求参数异常
     *
     * @param request
     * @param ex
     * @return
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResponseResult handleMethodArgumentTypeMismatchException(HttpServletRequest request, MethodArgumentTypeMismatchException ex) {
        log.info("MethodArgumentTypeMismatchException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), ex.getClass(), ex.getMessage());
        return ResponseResult.builder().code((long) HttpServletResponse.SC_BAD_REQUEST)
                .message("方法参数类型不匹配").build();
    }

    /**
     * 不支持的json数据格式异常
     *
     * @param request
     * @param ex
     * @return
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseResult handleHttpMessageNotReadableException(HttpServletRequest request, HttpMessageNotReadableException ex) {
        log.info("HttpMessageNotReadableException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), ex.getClass(), ex.getMessage());
        return ResponseResult.builder().code((long) HttpServletResponse.SC_NOT_ACCEPTABLE)
                .message("json解析错误").build();
    }

    /**
     * ShiroException
     *
     * @param request
     * @param ex
     * @return
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ShiroException.class)
    public ResponseResult handleShiroException(HttpServletRequest request, ShiroException ex) {
        log.info("ShiroException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), ex.getClass(), ex.getMessage());
        return ResponseResult.builder().code((long) HttpServletResponse.SC_BAD_REQUEST)
                .message("权限不足").build();
    }

    /**
     * AuthenticationException
     *
     * @param request
     * @param ex
     * @return
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(AuthenticationException.class)
    public ResponseResult handleHttpMessageNotReadableException(HttpServletRequest request, AuthenticationException ex) {
        log.info("AuthenticationException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), ex.getClass(), ex.getMessage());
        return ResponseResult.builder().code((long) HttpServletResponse.SC_NOT_ACCEPTABLE)
                .message("身份验证失败").build();
    }

    /**
     * @param request
     * @param ex
     * @return com.wootion.cims.response.ResponseResult
     * @description 捕捉shiro的异常, 没有有权限访问
     * @date 2022/10/27 0027
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(UnauthenticatedException.class)
    public ResponseResult handleUnauthenticatedException(HttpServletRequest request, UnauthenticatedException ex) {
        log.info("UnauthenticatedException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), ex.getClass(), ex.getMessage());
        return ResponseResult.builder().code((long) HttpStatus.UNAUTHORIZED.value())
                .message("未经身份验证").build();
    }

    /**
     * @param request
     * @param ex
     * @return com.wootion.cims.response.ResponseResult
     * @description token已经过期，请重新登录
     * @date 2022/10/27 0027
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(value = TokenExpiredException.class)
    public ResponseResult handlerTokenExpiredException(HttpServletRequest request, TokenExpiredException ex) {
        log.info("TokenExpiredException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), ex.getClass(), ex.getMessage());
        return ResponseResult.builder().code((long) HttpStatus.UNAUTHORIZED.value())
                .message("token已经过期，请重新登录").build();
    }

    /**
     * @param request
     * @param ex
     * @return kotlin.Result<java.lang.String>
     * @description 处理@Validated参数校验失败异常
     * @author lanqiang
     * @date 2022/11/7 0007
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResponseResult methodArgumentNotValidExceptionHandler(HttpServletRequest request, MethodArgumentNotValidException ex) {
        BindingResult result = ex.getBindingResult();
        StringBuilder stringBuilder = new StringBuilder("参数校验失败:");
        List<ObjectError> errors = result.getAllErrors();
        errors.forEach(p -> {
            FieldError fieldError = (FieldError) p;
            log.info("MethodArgumentNotValidException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), ex.getClass(), ex.getMessage());
            stringBuilder.append(":").append(fieldError.getDefaultMessage()).append(";");
        });
        String errorMsg = stringBuilder.toString();
        return ResponseResult.builder().code((long) HttpStatus.NOT_ACCEPTABLE.value())
                .message(errorMsg).build();
    }

    /**
     * @param request
     * @param ex
     * @return kotlin.Result<java.lang.String>
     * @description 处理@Validated参数校验失败异常
     * @author lanqiang
     * @date 2022/11/7 0007
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(value = ValidationException.class)
    public ResponseResult validationExceptionHandler(HttpServletRequest request, ValidationException ex) {
        log.info("validationException start, uri:{}, exception:{}, caused by: {}", request.getRequestURI(), ex.getClass(), ex.getMessage());
        return ResponseResult.builder().code((long) HttpStatus.NOT_ACCEPTABLE.value())
                .message(ex.getMessage()).build();
    }
}
